#include "DEV_Config.h"

static const char *TAG = "DEV_CFG";
spi_device_handle_t DEV_SPI = NULL;
static UBYTE s_dc_level = 1; /* last DC level; 0=command,1=data */
static UBYTE s_cs_level = 1; /* last CS level */

static void dev_gpio_set_output(gpio_num_t pin)
{
    gpio_config_t io = {
        .pin_bit_mask = (1ULL << pin),
        .mode = GPIO_MODE_OUTPUT,
        .pull_up_en = GPIO_PULLUP_DISABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    gpio_config(&io);
}

static void dev_gpio_set_input(gpio_num_t pin)
{
    gpio_config_t io = {
        .pin_bit_mask = (1ULL << pin),
        .mode = GPIO_MODE_INPUT,
        .pull_up_en = GPIO_PULLUP_ENABLE,
        .pull_down_en = GPIO_PULLDOWN_DISABLE,
        .intr_type = GPIO_INTR_DISABLE,
    };
    gpio_config(&io);
}

int DEV_Module_Init(void)
{
    /* Configure GPIOs */
    dev_gpio_set_output((gpio_num_t)EPD_RST_GPIO);
    dev_gpio_set_output((gpio_num_t)EPD_DC_GPIO);
    dev_gpio_set_output((gpio_num_t)EPD_CS_GPIO);
    dev_gpio_set_input((gpio_num_t)EPD_BUSY_GPIO);

    gpio_set_level((gpio_num_t)EPD_CS_GPIO, 1);
    gpio_set_level((gpio_num_t)EPD_DC_GPIO, 1);
    gpio_set_level((gpio_num_t)EPD_RST_GPIO, 1);

    /* Init SPI bus (VSPI) */
    spi_bus_config_t buscfg = {
        .mosi_io_num = EPD_MOSI_GPIO,
        .miso_io_num = -1,
        .sclk_io_num = EPD_SCLK_GPIO,
        .quadwp_io_num = -1,
        .quadhd_io_num = -1,
        .max_transfer_sz = 4096,
    };
    esp_err_t err = spi_bus_initialize(SPI3_HOST, &buscfg, SPI_DMA_CH_AUTO);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "spi_bus_initialize failed: %s", esp_err_to_name(err));
        return -1;
    }

    /* Attach device: manual CS via GPIO, mode0, ~8MHz */
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 8 * 1000 * 1000,
        .mode = 0,
        .spics_io_num = -1,
        .queue_size = 4,
        .flags = SPI_DEVICE_NO_DUMMY,
    };
    err = spi_bus_add_device(SPI3_HOST, &devcfg, &DEV_SPI);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "spi_bus_add_device failed: %s", esp_err_to_name(err));
        spi_bus_free(SPI3_HOST);
        return -2;
    }

    ESP_LOGI(TAG, "DEV_Module_Init OK (SCLK=%d, MOSI=%d, CS=%d, DC=%d, RST=%d, BUSY=%d)",
             EPD_SCLK_GPIO, EPD_MOSI_GPIO, EPD_CS_GPIO, EPD_DC_GPIO, EPD_RST_GPIO, EPD_BUSY_GPIO);
    return 0;
}

void DEV_Module_Exit(void)
{
    if (DEV_SPI) {
        spi_bus_remove_device(DEV_SPI);
        DEV_SPI = NULL;
    }
    spi_bus_free(SPI3_HOST);
}

void DEV_Digital_Write(UWORD Pin, UBYTE Value)
{
    gpio_set_level((gpio_num_t)Pin, Value ? 1 : 0);
    if (Pin == EPD_DC_PIN) { s_dc_level = Value ? 1 : 0; }
    if (Pin == EPD_CS_PIN) { s_cs_level = Value ? 1 : 0; }
}

UBYTE DEV_Digital_Read(UWORD Pin)
{
    int lvl = gpio_get_level((gpio_num_t)Pin);
    return (lvl != 0) ? 1 : 0;
}

void DEV_SPI_WriteByte(UBYTE data)
{
    if (!DEV_SPI) { return; }
    spi_transaction_t t = {
        .length = 8,
        .tx_buffer = &data,
    };
    /* Respect manual CS toggling done by vendor driver */
    esp_err_t err = spi_device_transmit(DEV_SPI, &t);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "spi_device_transmit err=%s", esp_err_to_name(err));
    }
    /* Optional: Log only commands (DC=0) to avoid huge logs */
    if (s_dc_level == 0 && s_cs_level == 0) {
        ESP_LOGI(TAG, "CMD 0x%02X", data);
    }
}
